package spring.data.jpa.repository.factory;

import java.io.Serializable;
import java.util.Optional;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import javax.persistence.EntityManager;

import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.util.StringUtils;

import spring.data.jpa.freemarker.FreemarkerResourceContext;

import com.slyak.spring.jpa.TemplateQueryLookupStrategy;

/**
 * {@link org.springframework.data.jpa.repository.support.JpaRepositoryFactory}
 *
 */
public class SimpleJpaRepositoryFactory extends JpaRepositoryFactory {

    private static final String INTERFACE = "repository.interface";
    private static final String CLASS = "repository.class";
    
    private final EntityManager entityManager;
    private final PersistenceProvider extractor;

    private Class<?> repositoryBaseClass;
    private Class<?> repositoryBaseInterface;
    private Class<?> domainClass;
    private FreemarkerResourceContext freemarkerContext;

    public void setDomainClass(Class<?> domainClass) {
        this.domainClass = domainClass;
    }

    public SimpleJpaRepositoryFactory(EntityManager entityManager, FreemarkerResourceContext freemarkerContext) {
        super(entityManager);
        
        this.entityManager = entityManager;
        this.extractor = PersistenceProvider.fromEntityManager(entityManager);
        
        this.freemarkerContext = freemarkerContext;
        
        initialize(null, null);
    }

    public SimpleJpaRepositoryFactory(EntityManager entityManager, Class<?> domainClass, Class<?> repositoryInterface,
            Class<?> repositoryClass, FreemarkerResourceContext freemarkerContext) {
        super(entityManager);
        this.domainClass = domainClass;
        
        this.entityManager = entityManager;
        this.extractor = PersistenceProvider.fromEntityManager(entityManager);
        
        this.freemarkerContext = freemarkerContext;
        
        initialize(repositoryInterface, repositoryClass);
    }

    @Override
    public RepositoryMetadata getRepositoryMetadata(Class<?> repositoryInterface) {
        if (this.domainClass == null) {
            return super.getRepositoryMetadata(repositoryInterface);
        } else {
            return new DomainRepositoryMetadata(this.domainClass);
        }
    }

    private void initialize(Class<?> repositoryInterface, Class<?> repositoryClass) {
        ResourceBundle bundle = PropertyResourceBundle.getBundle("simple-jpa");
        String interfaceName = bundle.getString(INTERFACE);
        String className = bundle.getString(CLASS);
        try {
            if (repositoryInterface != null) {
                this.repositoryBaseInterface = repositoryInterface;
            }
            if (StringUtils.hasText(interfaceName))
                this.repositoryBaseInterface = Thread.currentThread().getContextClassLoader().loadClass(interfaceName);
            else
                this.repositoryBaseInterface = JpaRepository.class;

            if (repositoryClass != null) {
                this.repositoryBaseClass = repositoryClass;
            }

            if (StringUtils.hasText(className))
                this.repositoryBaseClass = Thread.currentThread().getContextClassLoader().loadClass(className);
            else
                this.repositoryBaseClass = SimpleJpaRepository.class;
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("检查simple-jpa.properties的配置", e);
        }
    }

    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
        return repositoryBaseClass;
    }

    @Override
    protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
            QueryMethodEvaluationContextProvider evaluationContextProvider) {
        return Optional.of(TemplateQueryLookupStrategy.create(entityManager, key, extractor, evaluationContextProvider, freemarkerContext));
    }

    /**
     * 自动扫描创建时获取的方法
     * 
     * @param domainClass
     * @return
     */
    @SuppressWarnings({ "unchecked" })
    public <R extends JpaRepository<T, ID>, T, ID extends Serializable> R getObject() {
        return (R) this.getRepository(this.repositoryBaseInterface);
    }
}